#pragma once
#include <DataCore/sim_data.h>
#include <iostream>
#include <glm/glm.hpp>
//because Eigen will always have some problem with nvcc compiler,
//so we use forward definition to solve this problem until eigen's team finally solve their problem.
//#include <Eigen/Sparse>

namespace Eigen {
    template<typename _Scalar, int _Options, typename _StorageIndex> class SparseMatrix;
}

namespace PhysLeo {

/**
 * All physical model should extend this ForceModule interface.
 * deformEnergy and internalforce is basic which must be implemented by any subclass.
 * others are needed if you want to use implicit method or projective method.
 */
template<typename T>
class ForceModule {
public:
    /**
     * default empty constructor
     */
    ForceModule() = default;

    /**
     * delete default copy constructor
     */
    ForceModule(const ForceModule<T>&) = delete;

    /**
     * delete default move constructor
     */
    ForceModule(ForceModule<T>&&) = delete;

    /**
     * delete default copy assignment function
     */
    ForceModule<T>& operator=(const ForceModule<T>&) = delete;

    /**
     * delete default move assignment function
     */
    ForceModule<T>& operator=(ForceModule<T>&&) = delete;

    /**
     * virtual default destructor
     */
    virtual ~ForceModule() = default;

    /**
     * allocate resource for internal force module
     * @param[in] sim_data  simulation data
     */
    virtual void allocateResource(SimData<T>& sim_data) = 0;

    /** 
     * return the total deform energy of the system according to current pos.
     * @param[in] ptr_pos  current pos of system
     * @return a T value, the total energy
     */
    virtual T deformEnergy(std::shared_ptr<BufferData<glm::tvec3<T>>> ptr_pos) = 0;

    /**
     * return the internal force of the system according to current pos
     * @param[in] ptr_pos  current pos of system
     * @param[out] ptr_force  output the internalforce of the system
     */
    virtual void internalForce(std::shared_ptr<BufferData<glm::tvec3<T>>> ptr_pos, std::shared_ptr<BufferData<glm::tvec3<T>>> ptr_force) = 0;

    /**
     * return the stiffness matrix according to current pos
     * @param[in] ptr_pos  current pos of system
     * @return a Eigen SparseMatrix<T> ptr,  stiffness matrix
     */
    virtual Eigen::SparseMatrix<T,0,int>* stiffnessMatrix(std::shared_ptr<BufferData<glm::tvec3<T>>> ptr_pos)
    {
        //default implementation! if you want to use stiffness matrix without implement it first, report error.
        std::cout << "this force module don't implement compute Stiffness matrix function" << std::endl;
        assert(0);
        return nullptr;
    }

};

}